﻿using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;

namespace System
{
  [DebuggerStepThrough]
  public static class DataTableExtensions
  {
    public static DataTable ToDataTable<T>(this IList<T> items)
    {
      var tb = new DataTable(typeof (T).Name);

      PropertyInfo[] props = typeof (T).GetProperties(BindingFlags.Public | BindingFlags.Instance);

      foreach (PropertyInfo prop in props)
      {
        Type t = prop.PropertyType.GetCoreType();
        tb.Columns.Add(prop.Name, t);
      }

      foreach (T item in items)
      {
        var values = new object[props.Length];

        for (int i = 0; i < props.Length; i++)
        {
          values[i] = props[i].GetValue(item, null);
        }

        tb.Rows.Add(values);
      }

      return tb;
    }
    public static T Field<T>(this DataRow row, string columnName, T defaultValue)
    {
      try
      {
        return row.Field<T>(columnName);
      }
      catch
      {
        return defaultValue;
      }
    }
    public static T Field<T>(this DataRow row, string columnName)
    {
      if (row[columnName] == null)
        throw new NullReferenceException(columnName + " does not exist in DataRow");

      string value = row[columnName].ToString();

      if (typeof (T) == "".GetType())
      {
        return (T) Convert.ChangeType(value, typeof (T));
      }
      else if (typeof (T) == 0.GetType())
      {
        return (T) Convert.ChangeType(int.Parse(value), typeof (T));
      }
      else if (typeof (T) == false.GetType())
      {
        return (T) Convert.ChangeType(bool.Parse(value), typeof (T));
      }
      else if (typeof (T) == DateTime.Now.GetType())
      {
        return (T) Convert.ChangeType(DateTime.Parse(value), typeof (T));
      }
      else if (typeof (T) == new byte().GetType())
      {
        return (T) Convert.ChangeType(byte.Parse(value), typeof (T));
      }
      else if (typeof (T) == new float().GetType())
      {
        return (T) Convert.ChangeType(float.Parse(value), typeof (T));
      }
      else
      {
        throw new ArgumentException(string.Format("Cannot cast '{0}' to '{1}'.", value, typeof (T)));
      }
    }
    public static bool HasRows(this DataSet dataSet)
    {
      return (dataSet.Tables.Count > 0 && dataSet.Tables[0].Rows.Count > 0);
    }

    /// <summary>
    /// This method takes a csv string (comma separated) and turns into a DataTable.
    /// </summary>
    /// <param name="csvString">The CSV string (comma separated).</param>
    /// <param name="tableName">Name of the table.</param>
    /// <returns></returns>
    public static DataTable CsvToTable(string csvString, string tableName)
    {
      var dtResult = new DataTable();
      dtResult.TableName = tableName;

      using (var csv = new CachedCsvReader(new StringReader(csvString), true))
      {
        string[] headers = csv.GetFieldHeaders();

        foreach (string header in headers)
          dtResult.Columns.Add(header, typeof(string));

        while (csv.ReadNextRecord())
        {
          var newRow = dtResult.NewRow();
          for (int columnNumber = 0; columnNumber < headers.GetLongLength(0); columnNumber++)
            newRow[columnNumber] = csv[columnNumber];
          dtResult.Rows.Add(newRow);
        }
      }

      return dtResult;
    }

    public static string TableToCsv(DataTable table)
    {
      var sb = new StringBuilder();

      // We create the header record
      for (int counter = 0; counter < table.Columns.Count; counter++)
      {
        // We separate all but the first field by a comma
        if (counter > 0) { sb.Append(","); }
        // We add the field name
        sb.Append(table.Columns[counter].ColumnName.Trim());
      }
      sb.Append("\r\n");

      // We iterate over all rows, and all fields/columns
      for (int counter = 0; counter < table.Rows.Count; counter++)
      {
        var row = table.Rows[counter];
        for (int counter2 = 0; counter2 < table.Columns.Count; counter2++)
        {
          string field;
          field = row[counter2].ToString().Trim().Replace("\"", "\"\"");
          if (field.IndexOf(",") > 0 || field.IndexOf("\"") > 0 || field.IndexOf("\r") > 0 || field.IndexOf("\n") > 0)
            field = "\"" + field + "\"";

          // For all fields but the first, we need to add a comma-separator
          if (counter2 > 0) { sb.Append(","); }

          // We add the field value to the output stream.
          sb.Append(field);
        }
        // End of record. We add a line feed
        sb.Append("\r\n");
      }

      return sb.ToString();
    }

  }
}